
package com.kaigejava.rulesengine.engine.core.kie;

import com.kaigejava.rulesengine.engine.core.utils.KieUtils;
import org.drools.compiler.kie.builder.impl.InternalKieModule;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieRepository;
import org.kie.api.builder.ReleaseId;
import org.kie.api.definition.KiePackage;
import org.kie.api.definition.process.Process;
import org.kie.api.definition.rule.Rule;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.StatelessKieSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collection;

/**
 * @author kaigejava
 * @des:singleton class, by this class can get KieService,KieContainer and so on
 */
public class KieContex {
    private static Logger logger = LoggerFactory.getLogger(KieContex.class);
    /**
     * 有状态会话
     */
    public static final boolean STATEFULE_SESSION = true;
    /**
     * 无状态会话
     */
    public static final boolean STATELESS_SESSION = false;
    private static volatile KieContex kieContex;

    /**
     * iot kie contex status 0:normal 1:recompile
     */
    private volatile KieContexEnv kieStatus;

    private KieContexEnv sessionStatus;

    /**
     * KieService
     */
    private KieServices ks;

    /**
     * releaseId
     */
    private ReleaseId releaseId;

    /**
     * kJar
     */
    private InternalKieModule kModule;

    private KieRepository kRepo;

    /**
     * kie container
     */
    private KieContainer kContainer;

    private KieContex() {
    }

    /**
     * @return
     * @des double check singleton pattern
     */
    public static KieContex getKieContex() {
        if (kieContex == null) {
            synchronized (KieContex.class) {
                if (kieContex == null) {
                    kieContex = new KieContex();
                }
            }
        }
        return kieContex;
    }

    /**
     * 加载规则或规则变化时 初始化规则引擎环境
     *
     * @param stateFuleSession true 有状态会话 false 无状态会话
     *                         KieContexEnv.STATEFULE_SESSION
     *                         KieContexEnv.STATELESS_SESSION
     * @throws Exception
     */
    public synchronized void initKieContex(boolean stateFuleSession) throws Exception {
        setKieStatus(KieContexEnv.BUSY);
        setSessionStatus(stateFuleSession ? KieContexEnv.STATEFULE_SESSION : KieContexEnv.STATELESS_SESSION);
        logger.info("RulesEngine#### init KieContex begin.");
        ks = KieServices.Factory.get();
        releaseId = ks.newReleaseId("com.kaigejava.rulesengine", "rulesengine", "1.0.0");
        kModule = KieUtils.initKieJar(ks, releaseId, stateFuleSession);
        kRepo = ks.getRepository();
        kRepo.addKieModule(kModule);
        kContainer = ks.newKieContainer(releaseId);
        setKieStatus(KieContexEnv.IDLE);
        logger.info("RulesEngine#### init KieContex end.");
    }

    public KieContainer getKieContainer() {
        return kContainer;
    }

    /**
     * @return
     */
    public StatelessKieSession getJobSession() {
        return kContainer.getKieBase().newStatelessKieSession();
    }

    /**
     * get job session
     *
     * @param kBaseName
     * @return
     */
    public StatelessKieSession getJobSession(String kBaseName) {
        return kContainer.getKieBase(kBaseName).newStatelessKieSession();
    }

    public KieSession getJobStatefulSession(Long formatId) {
        return kContainer.getKieBase("kbase" + formatId).newKieSession();
    }

    /**
     * @param formatId
     * @return
     * @des get KieBase
     */
    private KieBase getKieBase(Long formatId) {
        return kContainer.getKieBase("kbase" + formatId);
    }

    public KieSession getJobStatefulSession() {
        return kContainer.getKieBase().newKieSession();
    }

    /**
     * @throws IOException
     * @des now recreate kjar,next updateToVersion by kcontainer
     */
    private void updateKieEnv() throws Exception {
        // if init what?now sleep ,
        while (this.getKieStatus() == KieContexEnv.BUSY) {
            try {
                Thread.sleep(1000);
                logger.warn("RulesEngine#### now init iot kie env ,busi.waitting...");
            } catch (InterruptedException e) {
                logger.error("UpdateKieEnvFailed,message:{}", e);
            }
        }

        initKieContex(isSessionStateful());
    }

    public KieBase getIotKieBase() {
        return kContainer.getKieBase();
    }

    /**
     * @param kieBase
     */
    public void explainKieBase(KieBase kieBase) {
        if (kieBase == null) {
            return;
        }
        Collection<Process> processes = kieBase.getProcesses();
        if (processes == null || processes.isEmpty()) {
            logger.error("kieBase contained NO processes.");
        } else {
            logger.info(MessageFormat.format("Found {0,number} processes:", processes.size()));
            for (Process process : processes) {
                logger.info(MessageFormat.format("{0}:{1} - {2} - {3}",
                        process.getPackageName(), process.getName(), process.getType(), process.getVersion()));
            }
        }
        Collection<KiePackage> packages = kieBase.getKiePackages();
        if (packages == null || packages.isEmpty()) {
            logger.error("kieBase contained NO packages.");
        } else {
            logger.info(MessageFormat.format("Found {0,number} packages:", packages.size()));
            for (KiePackage p : packages) {
                logger.info(MessageFormat.format("{0}", p.getName()));
                Collection<Rule> rules = p.getRules();
                if (rules == null || rules.isEmpty()) {
                    logger.error("kieBase:package contained NO rules.");
                } else {
                    for (Rule rule : rules) {
                        logger.info(MessageFormat.format("RuleCondition {0}:{1}",
                                rule.getPackageName(), rule.getName()));
                    }

                }
            }
        }
    }

    public boolean isBusy() {
        return kieStatus == KieContexEnv.BUSY ? true : false;
    }

    private enum KieContexEnv {
        /**
         * IoT kie env busying,recreating.
         */
        BUSY(1, "IoT kie env busying,recreating."),
        /**
         * IoT kie env idle,can using.
         */
        IDLE(0, "IoT kie env idle,can using."),
        /**
         * Now using Kie stateful session.
         */
        STATEFULE_SESSION(2, "Now using Kie stateful session."),
        /**
         * Now using Kie stateless session.
         */
        STATELESS_SESSION(3, "Now using Kie stateless session.");

        /**
         * @return the status
         */
        public int getStatus() {
            return status;
        }

        /**
         * @param status the status to set
         */
        public void setStatus(int status) {
            this.status = status;
        }

        /**
         * @return the msg
         */
        public String getMsg() {
            return msg;
        }

        /**
         * @param msg the msg to set
         */
        public void setMsg(String msg) {
            this.msg = msg;
        }

        private int status;
        private String msg;

        private KieContexEnv(int status, String msg) {
            this.status = status;
            this.msg = msg;
        }

    }

    /**
     * @return the kieStatus
     */
    public KieContexEnv getKieStatus() {
        return kieStatus;
    }

    /**
     * @param kieStatus the kieStatus to set
     */
    public void setKieStatus(KieContexEnv kieStatus) {
        this.kieStatus = kieStatus;
    }

    /**
     * @return the sessionStatus
     */
    public boolean isSessionStateful() {
        return sessionStatus == KieContexEnv.STATEFULE_SESSION ? true : false;
    }

    public void setSessionStatus(KieContexEnv sessionType) {
        sessionStatus = sessionType;
    }

}
